package com.wrupple.muba.catalogs.client.module.services.presentation.impl;

import java.util.List;
import java.util.Set;

import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.view.client.AsyncDataProvider;
import com.google.gwt.view.client.HasData;
import com.google.gwt.view.client.ProvidesKey;
import com.google.gwt.view.client.Range;
import com.wrupple.muba.catalogs.client.module.services.presentation.FilterableDataProvider;
import com.wrupple.muba.catalogs.domain.CatalogEntry;
import com.wrupple.muba.common.client.application.DataCallback;
import com.wrupple.muba.common.domain.FilterData;
import com.wrupple.muba.common.shared.State.StorageManager;

public class SimpleFilterableDataProvider<T extends JavaScriptObject>
		extends AsyncDataProvider<T> implements FilterableDataProvider<T> {
	

	protected class RangeReceived extends DataCallback<List<CatalogEntry>> {

		private FilterData filter;
		private Range servingRange;

		public RangeReceived(FilterData filter, Range currentRange) {
			this.filter=filter;
			this.servingRange=currentRange;
		}

		@Override
		public void execute() {
			callback =null;
			if (result != null) {
				List<T> processedResult=processRawEntries(result,filter);
				int rangeStart=filter.getStart();
				updateRowData(rangeStart, processedResult);
			}
		}
	}
	protected String catalog;
	private StorageManager storageManager;
	private FilterData filter;
	private boolean fetchAsSummary;
	private RangeReceived callback;

	public SimpleFilterableDataProvider(StorageManager storageManager,
			ProvidesKey<T> keyprovider) {
		super(keyprovider);
		this.fetchAsSummary = true;
		this.storageManager = storageManager;
		this.filter = new FilterData();
		this.filter.setConstrained(true);
	}

	protected List<T> processRawEntries(List<CatalogEntry> result, FilterData filter) {
		// simple processor, subclasses may disagree
		return (List<T>) result;
	}

	public void forceUpdateOnDisplays() {
		Set<HasData<T>> displays = super.getDataDisplays();
		for (HasData<T> display : displays) {
			forceUpdate(display);
		}
	}

	public String getCatalog() {
		return catalog;
	}


	public FilterData getFilter() {
		return filter;
	}
	
	public void setCatalog(String catalog) {
		assert catalog!=null ;
		this.catalog = catalog;
	}
	
	public void setFilter(FilterData filter) {
		this.filter = filter.copyExcludingRange();
	}
	

	private void forceUpdate(HasData<T> display) {
		Range range = display.getVisibleRange();
		display.setVisibleRangeAndClearData(range, true);
	}

	@Override
	protected void onRangeChanged(HasData<T> display) {
		final Range currentRange = display.getVisibleRange();
		
		
		if(callback==null||!callback.servingRange.equals(currentRange)){
			
			
			int displayedItemCount = display.getVisibleItemCount();
			
			
			FilterData filter = this.filter.copyExcludingRange();

			filter.setConstrained(true);
			
			calculateRangeToFetch(displayedItemCount,currentRange,filter);
			
			callback = new RangeReceived(filter,currentRange);
			fetch(filter);
		}

	}

	private void calculateRangeToFetch(int displayedItemCount,
			Range currentRange, FilterData filter) {
		int requestedStart=currentRange.getStart();
		
		int start= displayedItemCount;
		int end =requestedStart+currentRange.getLength() ;
		
		filter.setStart(start);
		filter.setEnd(end);
		
	}

	protected void fetch(FilterData filter) {
		assert catalog != null : "Catalog must not be null";
		GWT.log("Requesting entries from "+filter.getStart()+" to "+filter.getEnd()+" of "+catalog+" with restrictions "+filter.getFilters());
		if (fetchAsSummary&&filter.getJoins()==null) {
			storageManager.summary(catalog, filter, callback);
		} else {
			storageManager.read(catalog, filter, callback);
		}
	}

	public void setFetchSummary(boolean fetchAsSummary) {
		this.fetchAsSummary = fetchAsSummary;
	}

}
